﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Compiler.Unification.Constraints;
using Compiler.Unification.Substitutions;
using Compiler.TableOfSymbols;
using Compiler.TableOfSymbols.Types;
using ImportCode;

namespace Compiler.Unification
{
    public class ConstraintsQueue
    {
        private NETTypesBijectiveMap typesBijectiveMap;
        private PriorityQueue<BaseTypeConstraint> constraints;

        public ConstraintsQueue(NETTypesBijectiveMap typesBijectiveMap, HashSet<BaseTypeConstraint> constraints)
        {
            if (typesBijectiveMap == null)
                throw new ArgumentNullException("typesBijectiveMap can not be null");
            if (constraints == null)
                throw new ArgumentNullException("constraints can not be null");
            this.typesBijectiveMap = typesBijectiveMap;
            this.constraints = new PriorityQueue<BaseTypeConstraint>();
            foreach (BaseTypeConstraint constraint in constraints)
                this.constraints.Enqueue(constraint);
        }

        public bool IsEmpty
        { 
            get { return constraints.Count() == 0; } 
        }

        public void Enqueue(BaseTypeConstraint newConstraint)
        {
            if (newConstraint == null)
                throw new ArgumentNullException("newConstraint can not be null");
            this.constraints.Enqueue(newConstraint);
        }

        public BaseTypeConstraint Dequeue()
        {
            if (IsEmpty)
                throw new InvalidOperationException("Constraints queue is empty");
            return constraints.Dequeue();
        }

        public BaseTypeConstraint Peek()
        {
            if (IsEmpty)
                throw new InvalidOperationException("Constraints queue is empty");
            return constraints.Peek();
        }

        public void ApplySubstitutionItem(SubstitutionItem subsItem)
        {
            if (subsItem == null)
                throw new ArgumentNullException("subsItem can not be null");
            PriorityQueue<BaseTypeConstraint> newConstraints = new PriorityQueue<BaseTypeConstraint>();
            while (!IsEmpty)
            {
                BaseTypeConstraint curr = this.constraints.Dequeue();
                newConstraints.Enqueue(curr.MakeNewConstraint(
                    typesBijectiveMap.TypeSubstitution(curr.LeftTypeArgument, subsItem),
                    typesBijectiveMap.TypeSubstitution(curr.RightTypeArgument, subsItem)));
            }
            this.constraints = newConstraints;
        }

        private string _ConstraintToString(BaseTypeConstraint constraint)
        {
            return typesBijectiveMap.GetTypeName(constraint.LeftTypeArgument)
                + constraint.OperatorString 
                + typesBijectiveMap.GetTypeName(constraint.RightTypeArgument);
        }

        public override string ToString()
        {
            return constraints.ToString(_ConstraintToString);
        }
    }
}
